Object-oriented programming
Human intellectual capacity is exceeded by the complexity of most modern systems. This creates the need for computers and software. The fundamental task of software developers is to create the illusion of simplicity--to shield users from vast and often arbitrary external complexity. Programming transfers the routine handling of complexity from those who work with real-world systems to programmers who simulate these systems within computers. The problems are made no less complex by being moved inside of a computer. Only complex computer programs can solve problems that are complex in the real world.
When creating complex software systems, programmers suffer the same intellectual limits as other humans. It is therefore essential for programmers to decompose complex systems into smaller and smaller parts or modules, each of which may then be understood and refined independently. The fundamental task for developers of software tools then is to control and contain the inherent complexity of software.
Structured programming and top-down design typify the previous generation of modular program design. Using this approach, processes or algorithms are broken down into simpler and simpler sub-processes. These processes are then implemented and strung together like beads on a string to accomplish the complex overall task.
Object-oriented programming (OOP) also strives to decompose complex systems into more manageable sub-systems. Object-oriented programmers, however, view the world as a set of autonomous agents that collaborate to perform some higher level behavior. Only through the construction of meaningful collections of these agents is higher-level functionality achieved. When the behavior of the whole is greater than the sum of its parts this is emergent behavior. This gestalt is a powerful benefit of OOP. Simple collections of disconnected objects have little value. The valued added by a system comes from the relationships between the parts, not from the parts per se. Because of its method of non-linear decomposition, OOP is better suited to the random input encountered in a mouse and windows type interface.
More information on object-oriented design and programming is given in "Object-Oriented Analysis and Design with Applications--2.sup.nd ed.", by Grady Booch, (The Benjamin/Cummings Publishing Company, Inc., 1994. ISBN: 0-8053-5340-2) which is incorporated herein by reference.
Code Reuse
Because of the expense and effort involved in creating objects from scratch, reusing pre-built objects is highly desirable. In fact, the goal of programming complex applications by assembling reusable building blocks is nearly as old as computer science itself Attempting to emulate the reusability of erector sets, "Lego.RTM.", "Tinker-Toys.RTM." and electronics components, computer scientists have endeavored to create generic components from which more elaborate structures can be built up. While there has been some success in reuse, most programmers believe that there is still a long way to go.
Object-oriented programming has grown out of an effort to create computer languages that can more accurately model the way people naturally think about real world systems and objects. When objects in programs more accurately reflect objects in the real world, then the programming objects are more likely to be reusable in other contexts for which models of those real world objects are required. When programming objects contain elements that are extraneous to the real world object being modeled, then those extraneous portions may impede object reuse by creating dependencies that are not germane to the model.
Dependencies
An object in isolation is intensely uninteresting. To produce satisfactory emergent behavior, objects must collaborate. Objects collaborate through relationships called links. A link is a physical or conceptual connection between objects. A link denotes the specific association through which one object (the client) petitions the services of another object (the server). For each link, there is a client (the object that requests the transaction) and a server (the object that services the client's requests).
The server object must be visible to the client. This is typically accomplished in one of four methods: 1) The server object is global to the client, 2) The server object is a parameter to some operation of the client. 3) The server is part of the client or 4) The server is a locally declared object in some operation of the client.
Whenever one object passes a message to another across a link, the two objects are said to be synchronized. Synchronization of objects through links is the heart of an object-oriented program and the source of emergent behavior.
It is important to note that all four types of links mentioned above require the client object to write code calling the application programming interface (API) of the server object. In other words, clients are not independent of servers. In practical terms, this means that the client cannot be reused without also reusing the server. This reduces the reusability of the client object. Typically, an object will rely on the utilities of several server objects. Connecting to multiple servers has a cumulative effect of reducing reusability even further. This is because it becomes more likely that at least one of the servers will not be desirable in the new context. When servers are also clients, a web of dependencies is created throughout the program.
The impact of these dependencies on the code of the client objects can be reduced through the use of callbacks and virtual functions.
Callbacks are functions with a generic interface, often involving void pointers, that allow for the server to call the client back when something of interest happens. Callbacks keep the server from developing a dependency on the client since the client sets it up.
Virtual functions allow a subclass server to specialize the behavior of a base server without the client creating a dependency on the subclass server. Nevertheless, the client still has a dependency on the base class server. Callbacks and virtual functions can increase reusability of classes, but cannot entirely address the problem of dependencies without Herculean efforts. The end result is that reusability of these objects is impacted negatively because clients are required to be cognizant of servers to be able to access their services.
The Tradeoff
The desire to maintain object independence is strong for those wishing to employ object reuse. Most objects that are reused today are server objects. Many objects are both clients and servers. These objects cannot be reused as servers without bringing along all of the other objects on which they depend as clients. It is therefore desirable to reuse a client object as a server in another context without requiring that its servers be brought along.
In practice, reuse is made more difficult because linking object together is required in order to achieve the promised emergent behavior. Unfortunately, in today's most popular object-oriented languages, such as C++, all of these links must be hard coded, creating dependencies. The tension between object collaboration and object reuse is only partially resolvable, and then only by highly skilled object-oriented programmers. Most programmers aren't fully aware that the problem exists, even fewer can elucidate it, and virtually none address it on a daily basis. This is due mostly to limitations in the programming languages and paradigms that are commonly used and taught today.
Desirable Object-Oriented Features
C++ supports popular object-oriented features such as single and multiple inheritance, polymorphism, encapsulation, messaging, and data abstraction. These features are well described elsewhere so there is no need to redefine them here. Refer to "The C++ Programming Language (2.sup.nd edition)" by Bjarne Stroustrup (Addison Wesley--ISBN 0-201-53992-6) or "C++ Primer (2.sup.nd edition)" by Lippman, Stanley B. (Addison Wesley--ISBN 0-201-54848-8) which are incorporated herein by reference. These object-oriented language features are useful for solving large classes of common problems faced by programmers, however, they don't go far in averting the hard coded links between classes indicated previously. The hard coded dependencies required for object collaboration in C++ reduce the opportunity for object reuse.
Four features not currently available in C++, but implemented in some other object-oriented languages (and required as prerequisites for this invention) are:
meta-data (also referred to as introspection, reflection or run-time type information (RTTI)), PA1 full dynamic binding (also referred to as run-time or dynalink binding), PA1 probing (also referred to as left-monitored variables or write barriers), and PA1 instantiation of default objects by class name (also referred to as a generic factory method).
While not necessarily completing the object oriented programmer's dream list, these object oriented language enhancements enable powerful programming techniques to be applied that are not feasible without them. These enhancements used together with a system such as that described herein enable objects to collaborate without sacrificing their independence, facilitating object reuse and reducing program complexity.
Meta-Data is a database available at runtime (during program execution) that describes the classes used to build the program. It enables objects to be "self-describing" at runtime. When meta-data is extensible this creates an opportunity to have class wide read-only data that may be application specific. This application specific meta-data is called properties, and is an important language feature that is fully utilized in the invention. However, in language systems lacking extensible meta-data, property information can be easily stored in other data structures.
Full dynamic binding means that an object can be manipulated at runtime using only the names of its members. For example, attributes (fields or member fields in C++) can be set and queried, and functions can be called by name. Normal linking reduces the name to a machine address, and the name is lost from use to the program.
Probes are callback functions that are invoked when data (typically an object attribute) changes.
The ability to instantiate objects by name is also necessary for the complete implementation of the invention although this is easily implemented in languages that do not support it as a built in feature. Appendices A, B, C and D contain more lengthy discussions of these four object-oriented language features. Patent application Ser. No. 08/421,400 describes a method for enhancing languages (such as C++) that lack these features without changing the definition of the language and is incorporated herein by reference.
Together, these language features provide objects with the ability to have dynamically reconfigurable input and output during program execution without requiring special behavior on the part of the objects themselves. The term "dynamic language" is defined to mean a language that supports, or has been enhanced to support meta-data, full dynamic binding and probes.
Object Systems
There are many systems for creating dynamic "objects". These systems are referred to as "component-based" systems. There are platform object systems such as OLE (comprised of Microsoft's COM and OLE Automation) and OpenDoc (comprised correspondingly of IBM's SOM and Apple's Open Scripting Architecture) for the desktop, and Hewlett Packard's ORB+, Iona's ORBIX, NeXT's PDO and Sun's DOE for distributed applications. There are language based systems such as ViewSoft's Enhanced Object System (EOS), StepStone's Objective C and various SmallTalk implementations. There are dynamic linking systems such as Microsoft's DLLs and various flavors of unix shared libraries. There are also dynamic object systems designed for specialized requirements (e.g.--Lotus Notes for groupware and the SQL3 object model for databases). Finally, there are object systems that come built into applications such as Microsoft's VBX object system, Powersoft's PowerBuilder object system, the Novell AppWare Bus, etc.
In all of these systems, a "component" is a server object that is accessible via a dynamic interface during program execution. In most of these systems, the client must be especially written with the component system in mind, although in some of them (EOS, SmallTalk, PDO, DLL) client code is no different than when accessing another normal language based object; the system takes care of dynamically linking to the object. Often the dynamic linking is implemented in a language independent fashion, although they tend to do things as an object-oriented language would.
Some component systems allow callback architectures so that a client can install itself as a server for the component; in this way bi-directional communications may be established although the client typically has to provide a callback function, code to initialize the server with the callback and, in some cases, messages from the server must be decoded and dispatched by the client. Nevertheless, the rising popularity of these dynamic component based systems validates the basic approach of dynamically linking to components.
The ease with which one can create clients and components (server objects) in these systems ranges from trivial to fairly difficult.
Creating synchronizing links between two pure server components, (ie. comunication from one server to another server) requires creating client code that knows about both server objects. If the communication between the server objects is to be bidirectional, it requires the client to establish callbacks from both the servers or less elegantly code to poll the servers periodically. This client code is very specific to the task at hand, and is almost certainly non-reusable. If the client needs to transform information in order to make it suitable for both server objects, additional code will need to be written.
What is universally lacking in these component systems is a consistent method for creating generic clients that can dynamically attach to two server objects in such a way that the servers synchronize without creating dependencies on one another.
Client/Server Networking
Individual PCs or workstations connected via networking hardware and software such as local area networks (LANs) such as is incorporated in Microsoft's Windows 95.RTM. and Novell Netware, or on wide area networks (WANs) such as the internet are commonly available at this time. Many programs are made more efficient and useful by being distributed over networks. That is, if some of the objects in a program were on one machine and other objects were on a plurality of other machines, then the processing power of the network can be brought to bear more efficiently.
Often, programs are divided in two, a client and a server. The client program will typically be a GUI (Graphical User Interface) front end for a program running on the server, for example, a database server. Many such systems support multiple simultaneous clients. Network client/servers should not be confused with the client/server relationships between objects within a single program. Network client/server relationships are usually API (Application Programming Interface) based communications between programs rather than objects. These connections can be made via RPC (Remote Procedure Calls), sockets, or other commonly available APIs. RPC allows for code on one machine to call a function on another machine. This approach is good for flow of control, but the data sharing capabilities are somewhat limited. Sockets, in contrast, transfer data well, but are limited in program flow control.
The X-Windows system automates the separation of a GUI client from a processing server by automatically sending a bidirectional event stream over the network. This is a limited, but useful, architecture for distributing user interfaces for server programs over the network, but the network bandwidth required is quite high. Still, this level of network automation is deserving of emulation.
Networking Objects
Further division beyond a typical multiple client/single server architecture is also useful in many cases. Unfortunately, distributing programs with current API approaches is tedious for the programmer due to the fact that it is not normally integrated into the development environment or language. The learning curve for programmers to create robust client/server programs is typically quite large.
The current state of the art in distributed object systems are represented by Corba, DSOM, ORB+, ORBIX, PDO and DOE. These object systems are good at the data sharing and flow of control but are highly API based. Switching objects from one system to another is quite difficult due to the dependencies created between the program's objects and the object systems. That is, code specific to the object system must be written to connect and synchronize the objects across the network. Most of these systems also oblige the programmer to climb a steep learning curve.
Some object systems (notably PDO) have reached the point of having individual objects within programs address each other directly via name based dynamic binding. That is an object on machine A can access an attribute, or call a function of another object via dynamic binding by name on machine B. Creating connections directly between objects reduces the impedance mismatch between programming on one machine and programming in a distributed environment. I n other words, when objects within programs communicate directly over the network, no networking APIs need be employed because proxy objects handle the networking aspects without affecting the code within the objects themselves. This is a significant advancement in distributed programming as it frees the programmer from most of the effort of distributing the application over the network. It should be noted, however, that in this case, the client object must be aware of the public interface (API) of the server object and thus dependencies are created just as in client/server relationships between objects in the same program.